﻿using Antlr.Runtime;
using System;
using System.Collections.Generic;
using System.Collections;

namespace LPlusPlus
{
    partial class LPPParser
    {
        public int numErrors = 0;
        public SymTable tablaSimbolos = new SymTable();
        public int numVars = 0;
        public bool existeMetodoPrincipal = false;

        public override void ReportError(RecognitionException e)
        {
            numErrors++;
            base.ReportError(e);
            Console.WriteLine("Error en parser en linea: " + e.Line + ":" + e.CharPositionInLine + "->" + e);
        }

        public void errorTablaSimbolos(String msj)
        {
            numErrors++;
            Console.WriteLine("Error en parser: " + msj);
        }
    }

    public class Parametro
    {
        public string tipo;
        public string id;

        public Parametro(string _tipo, string _id)
        {
            tipo = _tipo;
            id = _id;
        }
    }

    public class Symbol
    {
        public int nro;
        public string tipo;
        public string modificador;
        public string clase;
        public string metodo;
        public string tipoRetorno;
        public int numParametros;
        public List<Parametro> parametros;

        //tipo = {clase, metodo, variable}
        //nro = numero en la tabla de simbolos
        //modificador = {publico, private}
        //clase = nombre de la clase a la cual pertenece una variable o metodo
        //metodo = nombre del metodo al cual pertenece una variable
        //tipoRetorno = {void, cadena, entero, booleano}
        //numParametros = numero de parametros que tiene un metodo
        //parametros = lista de parametros de un metodo
        public Symbol(string tipo, int nro, string modificador, string clase, string metodo, string tipoRetorno, int numParametros, List<Parametro> parametros)
        {
            this.tipo = tipo;
            this.nro = nro;
            this.modificador = modificador;
            this.clase = clase;
            this.metodo = metodo;
            this.tipoRetorno = tipoRetorno;
            this.numParametros = numParametros;
            this.parametros = parametros;
        }
    }

    public class MultiMap
    {
        Dictionary<string, List<Symbol>> _dictionary = new Dictionary<string, List<Symbol>>();

        public void Add(string key, Symbol value)
        {
            List<Symbol> list;
            if(this._dictionary.TryGetValue(key, out list))
            {
                foreach (Symbol v in list)
                {
                    if (value.tipo == "clase" && v.tipo == "clase")
                    {
                        //error clases con el mismo nombre
                        throw new Exception("Clases con el mismo nombre: " + key);
                    }
                    if (value.tipo != "clase" && v.tipo != "clase")
                    {
                        if(value.tipo != "metodo" && v.tipo != "metodo")
                        {
                            if(value.tipo == v.tipo)
                            {
                                if(value.clase == v.clase)
                                {
                                    if(value.metodo == null && v.metodo == null)
                                    {
                                        //error variables con el mismo nombre dentro de una clase
                                        throw new Exception("Variable repetida: " + key + " en la clase: " + v.clase);
                                    }
                                    else if(value.metodo == v.metodo)
                                    {
                                        if(value.numParametros == v.numParametros)
                                        {
                                            int cont = 0;
                                            for(int i = 0; i < v.numParametros; i++)
                                            {
                                                if(value.parametros[i].tipo == v.parametros[i].tipo)
                                                {
                                                    cont++;
                                                }
                                            }
                                            if(cont == v.numParametros)
                                            {
                                                //error variable definida en el mismo metodo de la misma clase
                                                throw new Exception("Variable repetida: " + key + " en la clase: " + v.clase + " y en el metodo: " + v.metodo);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (value.metodo == v.metodo && v.clase == value.clase)
                            {
                                if (value.numParametros == v.numParametros)
                                {
                                    int cont = 0;
                                    for (int i = 0; i < v.numParametros; i++)
                                    {
                                        if (value.parametros[i].tipo == v.parametros[i].tipo)
                                        {
                                            cont++;
                                        }
                                    }
                                    if (cont == v.numParametros)
                                    {
                                        //error metodo duplicado en el mismo metodo de la misma clase
                                        throw new Exception("Metodo repetido: " + key + " en la clase: " + v.clase);
                                    }
                                }
                            }
                        }
                    }
                }

                list.Add(value);
            }
            else
            {
                list = new List<Symbol>();
                list.Add(value);
                this._dictionary[key] = list;
            }
        }

        public IEnumerable<string> Keys
        {
            get
            {
                return this._dictionary.Keys;
            }
        }

        public List<Symbol> this[string key]
        {
            get
            {
                List<Symbol> list;
                if(!this._dictionary.TryGetValue(key, out list))
                {
                    list = new List<Symbol>();
                    this._dictionary[key] = list;
                }
                return list;
            }
        }
    }

    public class SymTable
    {
        public MultiMap symTable = new MultiMap();

        public string agregar(string id, Symbol des)
        {
            try
            {
                symTable.Add(id, des);
            }
            catch (Exception e)
            {
                return e.Message;
            }
            return null;
        }
    }

}
